#include "nxdat_xml_io.h"
namespace dx {
	class c_dx_io_xml_write_t
	{
	public:
		c_dx_io_xml_write_t(dx_stream_write_t* stream, dx_node_t* node)
			:_stream(stream)
		{
			write(node, 0);
		}
	private:
		void writeBuffer(void* buf, int l)
		{
			_stream->write(_stream->handle, buf, l);
		}
		void write_indent_tab(int indent)
		{
			for (int i = 0; i < indent; i++)
			{
				writeBuffer("\t", 1);
			}
		}
		void write_buffer_with_indent_tab(void* buf, int l, int indent)
		{
			write_indent_tab(indent); writeBuffer(buf, l);
		}

		void write_cell_int(dx_cell_t& cell)
		{
			static char buf[256];
			//writeBuffer("[", 1);
			if (cell.length == 1)
			{
				itoa(cell.iVal[0], buf, 10);
				writeBuffer(buf, strlen(buf));
			}
			else
			{
				for (int i = 0; i < cell.length; i++)
				{
					if (i > 0)writeBuffer(",", 1);
					itoa(cell.iVal[i], buf, 10);
					writeBuffer(buf, strlen(buf));
				}
			}
			//writeBuffer("]", 1);
		}

		void write_cell_real(dx_cell_t& cell)
		{
			static char buf[256];
			//writeBuffer("[", 1);
			if (cell.length == 1)
			{
				sprintf(buf, "%f", cell.rVal[0]);
				writeBuffer(buf, strlen(buf));
			}
			else
			{
				for (int i = 0; i < cell.length; i++)
				{
					if (i > 0)writeBuffer(",", 1);
					sprintf(buf, "%f", cell.rVal[i]);
					writeBuffer(buf, strlen(buf));
				}
			}
			//writeBuffer("]", 1);
		}
		void write_cell_vector2(dx_cell_t& cell)
		{
			static char buf[256];
			//writeBuffer("[", 1);
			if (cell.length == 1)
			{
				sprintf(buf, "[%f %f]", cell.vec2[0].x, cell.vec2[0].y);
				writeBuffer(buf, strlen(buf));
			}
			else
			{
				for (int i = 0; i < cell.length; i++)
				{
					if (i > 0)writeBuffer(",", 1);
					sprintf(buf, "%f %f", cell.vec2[i].x, cell.vec2[i].y);
					writeBuffer(buf, strlen(buf));
				}
			}
			//writeBuffer("]", 1);
		}
		void write_cell_vector3(dx_cell_t& cell)
		{
			static char buf[256];
			//writeBuffer("[", 1);
			if (cell.length == 1)
			{
				sprintf(buf, "[%f %f %f]", cell.vec3[0].x, cell.vec3[0].y, cell.vec3[0].z);
				writeBuffer(buf, strlen(buf));
			}
			else
			{
				for (int i = 0; i < cell.length; i++)
				{
					if (i > 0)writeBuffer(",", 1);
					sprintf(buf, "%f %f %f", cell.vec3[i].x, cell.vec3[i].y, cell.vec3[i].z);
					writeBuffer(buf, strlen(buf));
				}
			}
			//writeBuffer("]", 1);
		}

		void write_cell_vector4(dx_cell_t& cell)
		{
			static char buf[256];
			//writeBuffer("[", 1);
			if (cell.length == 1)
			{
				sprintf(buf, "[%f %f %f %f]", cell.vec4[0].x, cell.vec4[0].y, cell.vec4[0].z, cell.vec4[0].w);
				writeBuffer(buf, strlen(buf));
			}
			else
			{
				for (int i = 0; i < cell.length; i++)
				{
					if (i > 0)writeBuffer(",", 1);
					sprintf(buf, "%f %f %f %f", cell.vec4[0].x, cell.vec4[0].y, cell.vec4[0].z, cell.vec4[0].w);
					writeBuffer(buf, strlen(buf));
				}
			}
			//writeBuffer("]", 1);
		}

		void write_cell_string(dx_cell_t& cell)
		{
			if (cell.length == 1)
			{
				//writeBuffer("'", 1);
				writeBuffer((void*)cell.str, strlen((char*)cell.str));
				//writeBuffer("'", 1);
			}
			else
			{
				writeBuffer("[", 1);
				for (int i = 0; i < cell.length; i++)
				{
					if (i > 0)writeBuffer(",", 1);
					writeBuffer("\"", 1);
					writeBuffer((void*)cell.pstr[i], strlen((char*)cell.pstr[i]));
					writeBuffer("\"", 1);
				}
				writeBuffer("]", 1);
			}
		}

		void write_cell_wstring(dx_cell_t& cell)
		{
			if (cell.length == 1)
			{
				//writeBuffer("'", 1);
				string utf8 = utf16_to_utf8(cell.wstr);
				writeBuffer((void*)utf8.c_str(), utf8.size());
				//writeBuffer("'", 1);
			}
			else
			{
				writeBuffer("[", 1);
				for (int i = 0; i < cell.length; i++)
				{
					if (i > 0)writeBuffer(",", 1);
					writeBuffer("\"", 1);
					string utf8 = utf16_to_utf8(cell.pwstr[i]);
					writeBuffer((void*)utf8.c_str(), utf8.size());
					writeBuffer("\"", 1);
				}
				writeBuffer("]", 1);
			}
		}

		void write_data_cell(dx_cell_t& cell)
		{
			if (cell.type == dxint)
				write_cell_int(cell);
			else if (cell.type == dxreal)
				write_cell_real(cell);
			else if (cell.type == dxvector2)
				write_cell_vector2(cell);
			else if (cell.type == dxvector3)
				write_cell_vector3(cell);
			else if (cell.type == dxvector4)
				write_cell_vector4(cell);
			else if (cell.type == dxstring)
				write_cell_string(cell);
			else if (cell.type == dxwstring)
				write_cell_wstring(cell);
			else
			{
				_ASSERT(0);//never will happend
			}
		}

		void write(dx_node_t* node, int indent)
		{
			write_indent_tab(indent);
			writeBuffer("<node ", 6);

			writeBuffer(" name='", 7);//name
			nxid_t name = dxutils::get_name(node);
			writeBuffer((void*)name, strlen(name));
			writeBuffer("'", 1);

			if (dxutils::get_tag_count(node) > 0)
			{
				writeBuffer(" tags='", 7);//tags
				writeBuffer("[", 1);
				for (int i = 0, n = dxutils::get_tag_count(node); i < n; i++)
				{
					nxid_t tg = dxutils::get_tag(node, i);
					if (i > 0)writeBuffer(",", 1);
					writeBuffer("\"", 1);
					writeBuffer((void*)tg, strlen(tg));
					writeBuffer("\"", 1);
				}
				writeBuffer("]'", 2);
			}

			if (dxutils::get_meta(node) != NULL)//meta
			{
				const dx_node_meta_t* meta = dxutils::get_meta(node);

				writeBuffer(" meta='", 7);
				writeBuffer((void*)meta->name, strlen(meta->name));
				writeBuffer("'", 1);
			}
			writeBuffer(" type='", 7);//type
			e_dx_type_t ty = dxutils::get_type(node);
			const char* tystr = dx_type_to_string(ty);
			writeBuffer((void*)tystr, strlen(tystr));
			writeBuffer("'", 1);

			if (dxutils::is_data_cell(node))
			{
				char buf[256];
				sprintf(buf, " length='%d' ", dxutils::get_length(node));
				writeBuffer(buf, strlen(buf));
				writeBuffer(" cell='", 7);//type
				write_data_cell(dxutils::get_data_cell(node));
				writeBuffer("' />", 4);
			}
			else
			{
				writeBuffer(" >\n", 3);//type
				for (int i = 0, n = dxutils::get_length(node); i < n; i++)
				{
					if (i > 0)writeBuffer("\n",1);
					write_indent_tab(indent + 1);
					write(dxutils::get_child(node, i), indent + 1);
				}

				write_indent_tab(indent);
				writeBuffer("\n</node>\n", 9);
			}
		}
	public:
		dx_stream_write_t* _stream;
	};

	//read xml
	class c_dx_io_xml_read_t
	{
	public:
		c_dx_io_xml_read_t(dx_stream_read_t* stream, dx_node_t* parent) :_stream(stream)
		{
			string  strDoc;
			char	buf[1025];
			while (stream->read(stream->handle, (void*)buf, 1024) >0)
			{
				buf[1024] = 0;
				strDoc += string(buf);
			}
			mp::xml::xml_tree tree;
			bool succ = mp::xml::read_xml_string(strDoc, tree.root());
			_node = NULL;
			if (succ)
			{
				_node = read(parent, *tree.root().begin());
			}
		}
	private:
		void read_cell_int(dx_cell_t& cell, vector<string>& args)
		{
			if (cell.length == 1)
				cell.iVal[0] = atoi(args[0].c_str());
			else
			{
				for (int i = 0; i < cell.length; i++)
				{
					cell.iVal[i] = atoi(args[i].c_str());
				}
			}
		}

		void read_cell_real(dx_cell_t& cell, vector<string>& args)
		{
			if (cell.length == 1)
				cell.rVal[0] = atof(args[0].c_str());
			else
			{
				for (int i = 0; i < cell.length; i++)
				{
					cell.rVal[i] = atof(args[i].c_str());
				}
			}
		}

		void read_cell_vector2(dx_cell_t& cell, vector<string>& args)
		{
			if (cell.length == 1)
			{
				cell.vec2[0].x = atof(args[0].c_str());
				cell.vec2[0].y = atof(args[1].c_str());
			}
			else
			{
				for (int i = 0; i < cell.length; i++)
				{
					cell.vec2[i].x = atof(args[0 + i * 2].c_str());
					cell.vec2[i].y = atof(args[1 + i * 2].c_str());
				}
			}
		}
		void read_cell_vector3(dx_cell_t& cell, vector<string>& args)
		{
			if (cell.length == 1)
			{
				cell.vec3[0].x = atof(args[0].c_str());
				cell.vec3[0].y = atof(args[1].c_str());
				cell.vec3[0].z = atof(args[2].c_str());
			}
			else
			{
				for (int i = 0; i < cell.length; i++)
				{
					cell.vec3[i].x = atof(args[0 + i * 3].c_str());
					cell.vec3[i].y = atof(args[1 + i * 3].c_str());
					cell.vec3[i].z = atof(args[2 + i * 3].c_str());
				}
			}
		}
		void read_cell_vector4(dx_cell_t& cell, vector<string>& args)
		{
			if (cell.length == 1)
			{
				cell.vec4[0].x = atof(args[0].c_str());
				cell.vec4[0].y = atof(args[1].c_str());
				cell.vec4[0].z = atof(args[2].c_str());
				cell.vec4[0].w = atof(args[3].c_str());
			}
			else
			{
				for (int i = 0; i < cell.length; i++)
				{
					cell.vec4[i].x = atof(args[0 + i * 4].c_str());
					cell.vec4[i].y = atof(args[1 + i * 4].c_str());
					cell.vec4[i].z = atof(args[2 + i * 4].c_str());
					cell.vec4[i].w = atof(args[3 + i * 4].c_str());
				}
			}
		}

		void read_cell_string(dx_cell_t& cell, vector<string>& args)
		{
			if (cell.length == 1)
			{
				dxutils::put_string(cell.node, args[0].c_str(), 0);
			}
			else
			{
				for (int i = 0; i < cell.length; i++)
				{
					string& str = args[i];
					dxutils::put_string(cell.node, str.c_str(), i);
				}
			}
		}

		void read_cell_wstring(dx_cell_t& cell, vector<string>& args)
		{
			if (cell.length == 1)
			{
				wstring wstr = utf8_to_utf16(args[0].c_str());

				dxutils::put_wstring(cell.node, wstr.c_str(), 0);
			}
			else
			{
				for (int i = 0; i < cell.length; i++)
				{
					string& str = args[i];
					wstring wstr = utf8_to_utf16(str);
					dxutils::put_wstring(cell.node, wstr.c_str(), i);
				}
			}
		}

		void read_data_cell(dx_cell_t& cell,vector<string>& args)
		{
			if (cell.type == dxint)
				read_cell_int(cell, args);
			else if (cell.type == dxreal)
				read_cell_real(cell, args);
			else if (cell.type == dxvector2)
				read_cell_vector2(cell, args);
			else if (cell.type == dxvector3)
				read_cell_vector3(cell, args);
			else if (cell.type == dxvector4)
				read_cell_vector4(cell, args);
			else if (cell.type == dxstring)
				read_cell_string(cell, args);
			else if (cell.type == dxwstring)
				read_cell_wstring(cell, args);
		}

		dx_node_t*  read(dx_node_t* parnode,const mp::xml::xml_node& xnode)
		{
			nxid_t name = NULL;
			const dx_node_meta_t* meta = NULL;
			vector<nxid_t> tags;
			e_dx_type_t   ty = dxnull;
			int length = 1;
			{
				static string kname("name");
				string v=xnode.get_attr<string>(kname);
				name = nxid(v.c_str());
			}
			{//	
				static string kmeta("meta");
				if (xnode.has_attribute(kmeta))
				{
					string v = xnode.get_attr<string>(kmeta);
					nxid_t metaid = nxid(v.c_str());
					//TODO:get meta lib
				}
			}
			{
				static string ktags("tags");
				if (xnode.has_attribute(ktags))
				{
					Json::Reader reader;
					Json::Value  jsvalue;
					string v = xnode.get_attr<string>(ktags);
					reader.parse(v, jsvalue);
					for (int i = 0, n = jsvalue.size(); i < n; i++)
					{
						string str = jsvalue[i].asString();
						tags.push_back(nxid(str.c_str()));
					}
					
				}
			}
			{
				static string ktype("type");
				string v = xnode.get_attr<string>(ktype);
				ty = parse_dx_type(v.c_str());
				_ASSERT(ty != dxnull);
			}
			{
				static string klength("length");
				if(ty<dxnode)
					length = xnode.get_attr<int>(klength);

			}
			if (ty < dxnode)
			{
				static string kcell("cell");
				string cell = xnode.get_attr<string>(kcell);
				dx_node_t* nn = dxutils::add_child(parnode, name, ty, length);
				vector<string> args;
				if (ty == dxstring || ty == dxwstring)
				{
					if (length > 1)
					{
						Json::Value val;
						Json::Reader reader;
						reader.parse(cell, val);
						for (int i = 0; i < val.size(); i++)
						{
							args.push_back(val[i].asString());
						}
					}
					else
					{
						args.push_back(cell);
					}
					read_data_cell(dxutils::get_data_cell(nn), args);
				}
				else
				{
					tokenize(cell.c_str(), args, ", ");
					read_data_cell(dxutils::get_data_cell(nn), args);
				}
				return nn;
			}
			else
			{
				dx_node_t* nn = dxutils::add_child(parnode, name, ty, 0);
				for (mp::xml::xml_node::const_iterator it = xnode.begin(),
					itEnd = xnode.end(); it != itEnd; it++)
				{
					read(nn, *it);
				}
				return nn;
			}
		}
	public:
		dx_stream_read_t*		_stream;
		dx_node_t*				_node;
	};

}
extern "C"
{
	NXDAT_MINI_API
		int dx_write_node_to_xml(dx_stream_write_t* stream, dx_node_t* node)
	{
		dx::c_dx_io_xml_write_t write_(stream, node);
		return -1;
	}

	NXDAT_MINI_API
		dx_node_t* dx_read_node_from_xml(dx_stream_read_t* stream, dx_node_t* parent, dx_node_meta_lib_t* metalib)
	{
		dx::c_dx_io_xml_read_t read_(stream, parent);
		return read_._node;
	}
}